import Singleton from '../Base/Singleton'
import { ApiMsgEnum, binaryDecode, binaryEncode, IModel } from '../Common'

interface IItem {
  cb: Function
  ctx: unknown
}

interface IcallApiRes {
  success: boolean
  res?: any
  error?: Error
}

const PORT = 8080

export class NetworkManager extends Singleton {
  static get Instance() {
    return super.GetInstance<NetworkManager>()
  }

  ws: WebSocket
  map: Map<ApiMsgEnum, Array<IItem>> = new Map()
  isConnected: boolean = false

  connect() {
    return new Promise((resolve, reject) => {
      if (this.isConnected) {
        resolve(true)
        return
      }
      this.ws = new WebSocket(`ws://localhost:${PORT}`)
      // 设置websocket传输数据类型为二进制类型
      this.ws.binaryType = 'arraybuffer'
      this.ws.onopen = () => {
        this.isConnected = true
        resolve(true)
      }
      this.ws.onclose = () => {
        this.isConnected = false
        reject(false)
      }
      this.ws.onerror = () => {
        this.isConnected = false
        reject(false)
      }

      this.ws.onmessage = e => {
        try {
          const json = binaryDecode(e.data)
          const { name, data } = json
          if (this.map.has(name)) {
            this.map.get(name).forEach(({ cb, ctx }) => {
              cb.call(ctx, data)
            })
          }
        } catch (error) {
          console.log(error)
        }
      }
    })
  }

  // 调用api,将ws协议（订阅发布）改成http协议（异步请求）
  callApi<T extends keyof IModel['api']>(name: T, data: IModel['api'][T]['req']): Promise<IcallApiRes> {
    return new Promise((resolve, reject) => {
      try {
        const timer = setTimeout(() => {
          resolve({ success: false, error: new Error('Timeout!!') })
          this.unListenMsg(name as any, cb, null)
        }, 5000)

        const cb = (res: any) => {
          resolve(res)
          clearTimeout(timer)
          this.unListenMsg(name as any, cb, null)
        }
        this.listenMsg(name as any, cb, null)
        this.sendMsg(name as any, data)
      } catch (error) {
        reject({ success: false, error })
      }
    })
  }

  async sendMsg<T extends keyof IModel['msg']>(name: T, data: IModel['msg'][T]) {
    // 模拟网络延迟
    // await new Promise(resolve => setTimeout(resolve, 2000))
    const da = binaryEncode(name, data)
    this.ws.send(da.buffer)
  }

  listenMsg(name: ApiMsgEnum, cb: Function, ctx: unknown) {
    if (this.map.has(name)) {
      this.map.get(name).push({ cb, ctx })
    } else {
      this.map.set(name, [{ cb, ctx }])
    }
  }

  unListenMsg(name: ApiMsgEnum, cb: Function, ctx: unknown) {
    if (this.map.has(name)) {
      const index = this.map.get(name).findIndex(i => cb === i.cb && i.ctx === ctx)
      if (index > -1) {
        this.map.get(name).splice(index, 1)
      }
    }
  }
}
